#include "inputServer.h"
#include "eventServer.h"

using namespace sde;

InputServer::InputServer(std::string _type, std::string _name, Object* _object) :
    Action(_type,_name,_object),
    gfxServer(0),
    guiSystem(0),
    inputManager(0),
    keyboard(0),
    mouse(0),
    joystick(0) {
    gfxServer = (GfxServer*)Action::getFirstActionOfArchetype("gfx/GfxServer");
    if (!gfxServer)
        log("ERROR: couldn't find gfxServer");
    else {
        guiSystem = gfxServer->getGuiSystem();
    
        EventServer::getSingleton().subscribe("update",boost::bind(&InputServer::update,this,_1));
        Ogre::WindowEventUtilities::addWindowEventListener(gfxServer->getWindow(),this);
        
        size_t windowHnd = 0;
        std::ostringstream windowHndStr;
        OIS::ParamList pl;
            
        gfxServer->getWindow()->getCustomAttribute("WINDOW", &windowHnd);
        windowHndStr << windowHnd;
        pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
        
        #if defined OIS_WIN32_PLATFORM
        pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND" )));
        pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));
        pl.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_FOREGROUND")));
        pl.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_NONEXCLUSIVE")));
        #elif defined OIS_LINUX_PLATFORM
        pl.insert(std::make_pair(std::string("x11_mouse_grab"), std::string("false")));
        pl.insert(std::make_pair(std::string("x11_mouse_hide"), std::string("false")));
        pl.insert(std::make_pair(std::string("x11_keyboard_grab"), std::string("false")));
        pl.insert(std::make_pair(std::string("XAutoRepeatOn"), std::string("true")));
        #endif
        
        inputManager = OIS::InputManager::createInputSystem(pl);
    
        keyboard = static_cast<OIS::Keyboard*>(inputManager->createInputObject(OIS::OISKeyboard, true));
        mouse = static_cast<OIS::Mouse*>(inputManager->createInputObject(OIS::OISMouse, true));
        try {
            joystick = static_cast<OIS::JoyStick*>(inputManager->createInputObject(OIS::OISJoyStick, true));
        } catch (const OIS::Exception &e) {
        }
        
        if (keyboard)
            keyboard->setEventCallback(this);

        if (mouse)
            mouse->setEventCallback(this);

        if (joystick)
            joystick->setEventCallback(this);
    }
}

InputServer::~InputServer() {
    if (inputManager) {
        if (keyboard)
            inputManager->destroyInputObject(keyboard);
        if (mouse)
            inputManager->destroyInputObject(mouse);
        if (joystick)
            inputManager->destroyInputObject(joystick);
        OIS::InputManager::destroyInputSystem(inputManager);
    }
}

void InputServer::update(VarMap args) {
    Ogre::WindowEventUtilities::messagePump();
    
    if (keyboard) keyboard->capture();
    if (mouse) mouse->capture();
    if (joystick) joystick->capture();
}

void InputServer::windowClosed(Ogre::RenderWindow* rw) {
    log("window closed");
    object->shutdownSimulation();
}

bool InputServer::keyPressed(const OIS::KeyEvent& evt) {
    if (evt.key == OIS::KC_ESCAPE) {
        log("escape");
        object->shutdownSimulation();
    }
    
    if (guiSystem) {
        guiSystem->injectKeyDown(evt.key);
        guiSystem->injectChar(evt.text);
    }
    
    return true;
}

bool InputServer::keyReleased(const OIS::KeyEvent& evt) {
    if (guiSystem) {
        guiSystem->injectKeyUp(evt.key);
    }

    return true;
}

CEGUI::MouseButton InputServer::convertButton(OIS::MouseButtonID buttonID)
{
    switch (buttonID)
    {
    case OIS::MB_Left:
        return CEGUI::LeftButton;

    case OIS::MB_Right:
        return CEGUI::RightButton;

    case OIS::MB_Middle:
        return CEGUI::MiddleButton;

    default:
        return CEGUI::LeftButton;
    }
}

bool InputServer::mouseMoved(const OIS::MouseEvent& evt) {
    if (guiSystem) {
        guiSystem->injectMousePosition(evt.state.X.abs,evt.state.Y.abs);
    }
    return true;
}

bool InputServer::mousePressed(const OIS::MouseEvent& evt, OIS::MouseButtonID button) {
    if (guiSystem)
        guiSystem->injectMouseButtonDown(convertButton(button));

    return true;
}

bool InputServer::mouseReleased(const OIS::MouseEvent& evt, OIS::MouseButtonID button) {
    if (guiSystem)
        guiSystem->injectMouseButtonUp(convertButton(button));

    return true;
}

bool InputServer::buttonPressed(const OIS::JoyStickEvent& evt, int button) {
    return true;
}

bool InputServer::buttonReleased(const OIS::JoyStickEvent& evt, int button) {
    return true;
}

bool InputServer::axisMoved(const OIS::JoyStickEvent& evt, int axis) {
    return true;
}

void InputServer::windowResized(Ogre::RenderWindow* rw) {
    unsigned int width, height, depth;
    int left, top;
    rw->getMetrics(width, height, depth, left, top);

    if (mouse) {
        const OIS::MouseState &ms = mouse->getMouseState();
        ms.width = width;
        ms.height = height;
    }
}
